package com.cxcs.cxcsojbackendjudgeservice.judge.service.impl;

import cn.hutool.json.JSONUtil;
import com.cxcs.cxcsojbackendcommon.common.ErrorCode;
import com.cxcs.cxcsojbackendcommon.exception.BusinessException;
import com.cxcs.cxcsojbackendjudgeservice.judge.codesandbox.CodeSandBox;
import com.cxcs.cxcsojbackendjudgeservice.judge.codesandbox.CodeSandBoxFactory;
import com.cxcs.cxcsojbackendjudgeservice.judge.codesandbox.CodeSandBoxProxy;
import com.cxcs.cxcsojbackendjudgeservice.judge.service.JudgeService;
import com.cxcs.cxcsojbackendjudgeservice.judge.service.strategy.JudgeContext;
import com.cxcs.cxcsojbackendjudgeservice.judge.service.strategy.JudgeManager;
import com.cxcs.cxcsojbackendmodel.codesandbox.ExecuteCodeRequest;
import com.cxcs.cxcsojbackendmodel.codesandbox.ExecuteCodeResponse;
import com.cxcs.cxcsojbackendmodel.dto.question.JudgeCase;
import com.cxcs.cxcsojbackendmodel.dto.questionsubmit.JudgeInfo;
import com.cxcs.cxcsojbackendmodel.entity.Question;
import com.cxcs.cxcsojbackendmodel.entity.QuestionSubmit;
import com.cxcs.cxcsojbackendmodel.enums.JudgeInfoMessageEnum;
import com.cxcs.cxcsojbackendmodel.enums.QuestionSubmitStatusEnum;
import com.cxcs.cxcsojbackendserviceclient.service.QuestionFeignClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 判题服务 实现类
 *
 * @Author: CxCS
 * @Version: 1.0
 * @Date: 2024-08
 */
@Service
public class JudgeServiceImpl implements JudgeService {
    @Value("${codesandbox.type:example}")
    private String type;    // 读取 yml 文件 的代码沙箱配置
    @Resource
    private QuestionFeignClient questionFeignClient;
    @Resource
    private JudgeManager judgeManager;
    @Override
    public QuestionSubmit doJudge(long questionSubmitId) {
        // 1. 获取题目提交信息
        QuestionSubmit questionSubmit = questionFeignClient.getQuestionSubmitById(questionSubmitId);
        if(questionSubmit == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目提交信息不存在");
        }
        Long questionId = questionSubmit.getQuestionId();
        Question question = questionFeignClient.getQuestionById(questionId);
        if(question == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目不存在");
        }

        // 2.判题信息 不是等待状态，不用重复执行
        if(!questionSubmit.getStatus().equals(QuestionSubmitStatusEnum.WAITING.getValue())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "正在执行中，请耐心等待");
        }
        // 3.加锁 更新判题状态
        QuestionSubmit updateQuestionSubmit = new QuestionSubmit();
        updateQuestionSubmit.setId(questionSubmitId);
        updateQuestionSubmit.setStatus(QuestionSubmitStatusEnum.RUNNING.getValue()); // 更新为等待中
        boolean update = questionFeignClient.updateQuestionSubmitById(updateQuestionSubmit);
        if(!update) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "判题状态更新异常");
        }

        String language = questionSubmit.getLanguage();
        String code = questionSubmit.getCode();
        // 获取测试用例
        String judgeCaseStr = question.getJudgeCase();
        List<JudgeCase> judgeCaseList = JSONUtil.toList(judgeCaseStr, JudgeCase.class);
        List<String> inputList = judgeCaseList.stream().map(JudgeCase::getInput).collect(Collectors.toList()); // 获取其使用的输入用例

        // 4.调用代码沙箱
        CodeSandBox codeSandBox = CodeSandBoxFactory.newInstance(type);
        codeSandBox = new CodeSandBoxProxy(codeSandBox);  // 代理

        // 该实体类使用Builder注解
        ExecuteCodeRequest executeCodeRequest = ExecuteCodeRequest.builder()
                .code(code)
                .language(language)
                .inputList(inputList)
                .build();
        ExecuteCodeResponse executeCodeResponse = codeSandBox.executeCode(executeCodeRequest); // 沙箱执行响应信息

        // 更新题目（提交数、通过数）
        Question updateQuestion = new Question();
        updateQuestion = question;

        // 代码运行不成功
        if(executeCodeResponse.getStatus() != QuestionSubmitStatusEnum.SUCCEED.getValue()) {
            updateQuestionSubmit.setStatus(executeCodeResponse.getStatus());
            updateQuestionSubmit.setJudgeInfo(JSONUtil.toJsonStr(executeCodeResponse.getJudgeInfo()));

            // 运行成功，匹配用例
        } else {
            List<String> outputList = executeCodeResponse.getOutputList(); // 执行的输出结果
            // 5.根据沙箱响应信息，匹配用例 是否AC
            // 封装成judgeContext 上下文
            JudgeContext judgeContext = new JudgeContext();
            judgeContext.setJudgeInfo(executeCodeResponse.getJudgeInfo());
            judgeContext.setJudgeCaseList(judgeCaseList);
            judgeContext.setInputList(inputList);
            judgeContext.setOutputList(outputList);
            judgeContext.setQuestion(question);
            judgeContext.setQuestionSubmit(questionSubmit);
            // 交给判题策略 匹配（由策略工厂管理匹配）
            JudgeInfo judgeInfoResponse = judgeManager.doJudge(judgeContext);

            // 更新题目通过数
            if(judgeInfoResponse.getMessage().equals(JudgeInfoMessageEnum.ACCEPTED.getText())) {
                updateQuestion.setAcceptedNum(updateQuestion.getAcceptedNum() + 1);
            }

            // 6.更新数据库中的判题结果（加锁更新）
            updateQuestionSubmit = new QuestionSubmit();
            updateQuestionSubmit.setId(questionSubmitId);
            updateQuestionSubmit.setStatus(QuestionSubmitStatusEnum.SUCCEED.getValue()); // 判题 成功（完成）
            updateQuestionSubmit.setJudgeInfo(JSONUtil.toJsonStr(judgeInfoResponse)); // 更新判题结果
        }

        update = questionFeignClient.updateQuestionSubmitById(updateQuestionSubmit);
        if(!update) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "判题状态更新异常");
        }

        // 7.重新获取题目提交信息 返回
        QuestionSubmit questionSubmitResult = questionFeignClient.getQuestionSubmitById(questionSubmitId);

        // 8.更新题目 的提交数和通过数
        updateQuestion.setSubmitNum(updateQuestion.getSubmitNum() + 1);
        questionFeignClient.updateQuestionById(updateQuestion);

        return questionSubmitResult;
    }
}
